# -*- coding: utf8 -*-

import sys
import os
from threading import RLock
from Queue import Queue
import gtk
import gobject
import json
from worker.directory import DirectoryListWorker
from worker.file import FileListWorker
from worker.hash import FileHashCalcWorker
from worker.archive_image import ArchiveWorker
from project.sql import Project
from statistics import Statistics
from time import time
from datetime import timedelta, datetime
from pprint import pprint
import widgets
from send2trash import send2trash

print("Python version: %r" % (sys.version,) )
print("GTK+ version: %r" % (gtk.gtk_version,) )
print("PyGTK version: %r" % (gtk.pygtk_version,) )

class Main( object ):
    
    _options = None
    
    _queues = None
    _queues_lock = None
    
    _stats = None
    
    _threads = None
    _thread_ui = None
    _thread_ui_run = True
    
    _ui = None
    _ui_title = None
    _project = None
    
    _last_stats = None

    _file_prog = None
    _file_lbl = None

    def __init__( self ):
        self._options = {
            "ui_file" : "resource/ui.glade",
            "project" : {
                "adapter" : "mysql",
                "host" : "localhost",
                "db" : "filedup",
                "user" : "root",
                "passwd" : "root",
                "options" : "?charset=utf8"
            },
            "filter_in" : ["*.*"],
            "filter_out" : ["*.db"],
            "delete_to_trash" : 1,
            "hash_function" : "md5",
            "directories" : []
        }
        self._queues = {}
        self._queues_lock = RLock()
        self._threads = {}
        
        self.load_project()
        
        self._last_stats = {}

        self._file_prog = {}
        self._file_lbl = {}

        self._started = False
        
    def get_option( self, name ):
        return self._options[ name ]
    
    def get_queue( self, name ):
        if not self._queues.has_key( name ):
            self._queues[ name ] = Queue()
        return self._queues[ name ]

    def get_dirlist_queue( self, name, device ):
        self._queues_lock.acquire()
        qname = "%s_%s" % (name, device)
        if not self._threads.has_key(qname):
            th = DirectoryListWorker(self)
            th.my_query_name = qname
            th.start()
            self._threads[qname] = th
        q = self.get_queue(qname)
        self._queues_lock.release()
        return q

    def get_filelist_queue( self, name, device ):
        self._queues_lock.acquire()
        qname = "%s_%s" % (name, device)
        if not self._threads.has_key(qname):
            th = FileListWorker(self)
            th.my_query_name = qname
            th.start()
            self._threads[qname] = th
        q = self.get_queue(qname)
        self._queues_lock.release()
        return q

    def get_hashcalc_queue( self, name, device ):
        self._queues_lock.acquire()
        qname = "%s_%s" % (name, device)
        if not self._threads.has_key(qname):
            th = FileHashCalcWorker(self)
            th.my_query_name = qname
            th.start()
            self._threads[qname] = th
        q = self.get_queue(qname)
        self._queues_lock.release()
        return q

    def get_archive_queue( self, name, device ):
        self._queues_lock.acquire()
        qname = "%s_%s" % (name, device)
        if not self._threads.has_key(qname):
            th = ArchiveWorker(self)
            th.my_query_name = qname
            th.start()
            self._threads[qname] = th
        q = self.get_queue(qname)
        self._queues_lock.release()
        return q

    def get_stats( self ):
        if not self._stats:
            self._stats = Statistics()
        return self._stats

    def format_file_size( self, size ):
        """
        @rtype: tuple
        """
        u = "Б"
        if size > 1024:
            size /= 1024.0
            u = "КиБ"
        if size > 1024:
            size /= 1024.0
            u = "МиБ"
        if size > 1024:
            size /= 1024.0
            u = "ГиБ"
        if size > 1024:
            size /= 1024.0
            u = "ТиБ"
        return size, u
    
    def format_file_size_si( self, size ):
        """
        @rtype: tuple
        """
        u = "Б"
        if size > 1000:
            size /= 1000.0
            u = "КБ"
        if size > 1000:
            size /= 1000.0
            u = "МБ"
        if size > 1000:
            size /= 1000.0
            u = "ГБ"
        if size > 1000:
            size /= 1000.0
            u = "ТБ"
        return size, u


    def get_hash_algs(self):

        algs = [
            'md2', 'md4', 'md5', 'md6',
            'sha1', 'sha256', 'sha512',
            'ripemd160', 'ripemd320',
            'tiger', 'whirlpool'
        ]

        import hashlib

        support = []
        for alg in algs:
            try:
                hashlib.new(alg)
                support.append(alg)
            except:
                pass

        return support


    def get_ui( self ):
        """
        @rtype: gtk.Builder
        """
        if self._ui is None:
            self._ui = gtk.Builder()
            self._ui.add_from_file( self.get_option("ui_file") )

            self._ui_title = self._ui.get_object("window1").get_title()


            treeview1 = self._ui.get_object("treeview1")

            # add columns:
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("Каталог", cell0, text=0)
            treeview1.append_column(col0)
            
            store = self._ui.get_object("liststore1")
            treeview1.set_model(store)


            combo_hash = self._ui.get_object("combobox_hash")
            store = gtk.ListStore(
                gobject.TYPE_STRING,
            )
            combo_hash.set_model(store)
            cell = gtk.CellRendererText()
            combo_hash.pack_start(cell, True)
            combo_hash.add_attribute(cell, 'text', 0)

            for item in self.get_hash_algs():
                store.append([item])

            treeview3 = self._ui.get_object("treeview3")

            # add columns:
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("HID", cell0, text=0)
            treeview3.append_column(col0)
            
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("FID", cell0, text=1)
            treeview3.append_column(col0)
            
            cell_rend = widgets.CellRendererButton()
            col0 = gtk.TreeViewColumn("Удалить", cell_rend, callable=2, text=7, xpad=8, ypad=9)
            treeview3.append_column(col0)
            
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("Дата изменения", cell0, text=3)
            treeview3.append_column(col0)
            
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("Размер", cell0, text=4)
            treeview3.append_column(col0)
            
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("MIME", cell0, text=5)
            treeview3.append_column(col0)
            
            cell0 = gtk.CellRendererText()
            col0 = gtk.TreeViewColumn("Файл", cell0, text=6)
            treeview3.append_column(col0)
            
            #store = self._ui.get_object("treestore1")
            store = gtk.TreeStore(
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_PYOBJECT,
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_STRING,
                gobject.TYPE_INT,
                gobject.TYPE_INT,
                gobject.TYPE_INT,
            )
            treeview3.set_model(store)

            filter = ",".join(self._options["filter_in"])
            self._ui.get_object("entry_filter_in").set_text(filter)
            
            filter = ",".join(self._options["filter_out"])
            self._ui.get_object("entry_filter_out").set_text(filter)
            
            ff = self._ui.get_object("filefilter_project")
            ff.set_name("Projects")
            ff.add_pattern("*.db")
            
            self._ui.connect_signals(self)
        return self._ui
    
    def test_store(self):
        treeview3 = self._ui.get_object("treeview3")
        store = treeview3.get_model()
        iter = store.append( None, [ "hash_123", None, None, None, None, None, None, None, 0, 0 ] )
        print("iter 1: %r => %r" % (iter, store.get_path(iter), ))

        iter = store.append( iter, [ None, "file_345", self.idle_button_del_file_clicked, "2010-01-01 12:05", "123456", "mime", "file name", "Удалить", 2, 2 ] )
#        store.set(iter, 2, self.on_button_del_file_clicked)
        
        print("iter 1: %r => %r" % (iter, store.get_path(iter), ))

    
    
    def match_func(self, model, iter, data):
        column, key = data # data is a tuple containing column number, key
        value = model.get_value(iter, column)
        return value == key
    
    def search(self, model, iter, func, data):
        while iter:
            if func(model, iter, data):
                return iter
            result = self.search(model, model.iter_children(iter), func, data)
            if result: return result
            iter = model.iter_next(iter)
        return None
    
    
    def update_ui_stats( self ):
        
        stats = self.get_stats().get_update()
        
        ui = self.get_ui()
        
        gobject.idle_add( ui.get_object("label_dirs").set_label, "%s" % stats["directories_count"] )
        gobject.idle_add( ui.get_object("label_dirs_searched").set_label, "%s" % stats["directories_searched"] )
        
        gobject.idle_add( ui.get_object("label_files").set_label, "%s" % stats["files_count"] )
        gobject.idle_add( ui.get_object("label_files_filtered").set_label, "%s" % stats["files_filtered"] )
        gobject.idle_add( ui.get_object("label_hashed").set_label, "%s" % stats["hashed_count"] )
        gobject.idle_add( ui.get_object("label_errors").set_label, "%s" % stats["errors_count"] )
        gobject.idle_add( ui.get_object("label_unique").set_label, "%s" % stats["unique_count"] )
        gobject.idle_add( ui.get_object("label_nonunique").set_label, "%s" % stats["nonunique_count"] )

        gobject.idle_add( ui.get_object("label_archives").set_label, "%s" % stats["archives_count"] )
        gobject.idle_add( ui.get_object("label_archives_processed").set_label, "%s" % stats["archives_processed"] )
        gobject.idle_add( ui.get_object("label_images").set_label, "%s" % stats["images_count"] )
        gobject.idle_add( ui.get_object("label_images_processed").set_label, "%s" % stats["images_processed"] )

        if stats["files_size"]:
            p_all = stats["readed_size"] * 1.0 / stats["files_size"]
        else:
            p_all = 0
        pb_all = p_all
        if pb_all < 0:
            pb_all = 0
        if pb_all > 1:
            pb_all = 1
        gobject.idle_add( ui.get_object("progressbar_all").set_fraction, pb_all )

        gobject.idle_add( ui.get_object("window1").set_title,
            "[ %.2f %% ] %s" % ( p_all*100, self._ui_title, ) )

        if stats["nonunique_count"]:
            p_un = stats["nonunique_count_now"] * 1.0 / stats["nonunique_count"]
        else:
            p_un = 0
        if p_un < 0:
            p_un = 0
        if p_un > 1:
            p_un = 1
        gobject.idle_add( ui.get_object("progressbar_nonuniq").set_fraction, p_un )

        nuq_sz, nuq_un = self.format_file_size_si( stats["nonunique_size"] )
        nuqn_sz, nuqn_un = self.format_file_size_si( stats["nonunique_size_now"] )

        gobject.idle_add( ui.get_object("progressbar_nonuniq").set_text,
            "Сейчас: %d (%0.3f%s); Найдено: %d (%0.3f%s)" % (
                stats["nonunique_count_now"], nuqn_sz, nuqn_un,
                stats["nonunique_count"], nuq_sz, nuq_un,
                )
        )

        asz, au = self.format_file_size_si( stats["files_size"] )
        rsz, ru = self.format_file_size_si( stats["readed_size"] )

        if not len(self._last_stats):
            self._last_stats.update(stats)
            self._last_stats["start_size"] = stats["real_readed_size"]
            self._last_stats["start_time"] = time()-1
            self._last_stats["time"] = time()-1

        if stats["files_size"] != stats["readed_size"]:

            stats["time"] = time()

            bs = stats["real_readed_size"] - self._last_stats["start_size"]
            if bs > 0 and stats["time"] > self._last_stats["start_time"]:
                bs /= stats["time"] - self._last_stats["start_time"]
            else:
                bs = 1

            sps, spu = self.format_file_size_si( bs )
            spu += "/сек"

            if not bs:
                bs = 1
            rts = (stats["files_size"] - stats["readed_size"]) / bs

            gobject.idle_add( ui.get_object("progressbar_all").set_text,
                "Все файлы: %0.2f%% : %0.3f%s из %0.3f%s, %0.1f%s, ETA: %s" % (
                p_all*100, rsz, ru, asz, au, sps, spu, timedelta(seconds = rts))
            )
        else:
            gobject.idle_add( ui.get_object("progressbar_all").set_text,
                "Все файлы: %0.2f%% : %0.3f%s из %0.3f%s, Заняло: %s" % (
                    p_all*100, rsz, ru, asz, au, timedelta(seconds = (self._last_stats['time'] - self._last_stats['start_time'])))
            )

        hashing_files = stats["hashing_file"]

        for device, item in hashing_files.items():
            if item["size"]:
                p_cur = item["readed"] * 1.0 / item["size"]
            else:
                p_cur = 0
            pb_cur = p_cur
            if pb_cur < 0:
                pb_cur = 0
            if pb_cur > 1:
                pb_cur = 1
            gobject.idle_add( self._get_file_progress(device).set_fraction, pb_cur )

            file_sz, file_u = self.format_file_size_si( item["size"] )
            file_rsz, file_ru = self.format_file_size_si( item["readed"] )

            if item["size"] != item["readed"]:
                file_bs = item["readed"]
                if file_bs > 0 and stats["time"] > item["start_time"]:
                    file_bs /= stats["time"] - item["start_time"]
                else:
                    file_bs = 1

                file_sps, file_spu = self.format_file_size_si( file_bs )
                file_spu += "/сек"

                if not file_bs:
                    file_bs = 1
                file_rts = (item["size"] - item["readed"]) / file_bs


                gobject.idle_add( self._get_file_label(device).set_text,
                    "Файл: %s" % ( item["name"], )
                )

                gobject.idle_add( self._get_file_progress(device).set_text,
                    "Файл: %0.2f%% : %0.3f%s из %0.3f%s, %0.1f%s, ETA: %s" % (
                    p_cur*100, file_rsz, file_ru, file_sz, file_u, file_sps, file_spu,
                    timedelta(seconds = file_rts))
                )
            else:
                gobject.idle_add( self._get_file_progress(device).set_text,
                    "Файл: %0.2f%% : %0.3f%s из %0.3f%s" % (
                        p_cur*100, file_rsz, file_ru, file_sz, file_u)
                )

            pass


        images_archive = stats["images_archive"]
        if images_archive:

            item = images_archive

            if item["count"]:
                p_cur = item["processed"] * 1.0 / item["count"]
            else:
                p_cur = 0
            pb_cur = p_cur
            if pb_cur < 0:
                pb_cur = 0
            if pb_cur > 1:
                pb_cur = 1
            gobject.idle_add( self._get_file_progress("archive").set_fraction, pb_cur )

            gobject.idle_add( self._get_file_label("archive").set_text,
                "Файл: %s" % ( item["name"], )
            )

            if item["count"] != item["processed"]:

                stats["time"] = time()

                item_bs = item["processed"]
                if item_bs > 0 and stats["time"] > item["start_time"]:
                    item_bs /= stats["time"] - item["start_time"]
                else:
                    item_bs = 1

                if not item_bs:
                    item_bs = 1
                item_rts = (item["count"] - item["processed"]) / item_bs

                gobject.idle_add( self._get_file_progress("archive").set_text,
                    "Файл: %0.2f%% : %s из %s, ETA: %s" % (
                        p_cur*100, item["processed"], item["count"], timedelta(seconds=item_rts))
                )
            else:
                gobject.idle_add( self._get_file_progress("archive").set_text,
                    "Файл: %0.2f%% : %s из %s" % ( p_cur*100, item["processed"], item["count"], )
                )



        # Update last stats
        self._last_stats.update(stats)

        return stats


    def _clean_progress_ui(self):
        ui = self.get_ui()
        main_vbox = ui.get_object("vbox_files_progress")
        for child in main_vbox.get_children():
            child.destroy()
        self._file_lbl = {}
        self._file_prog = {}
        return


    def _add_file_progress_ui(self, device):
        ui = self.get_ui()

        main_vbox = ui.get_object("vbox_files_progress")

        vbox = gtk.VBox()

        lbl = gtk.Label("Файл:")
        lbl.set_alignment(0, 0.5)
        lbl.set_name("label_current_file_%s" % device)
        lbl.show()
        vbox.pack_start(lbl)

        prog = gtk.ProgressBar()
        prog.set_name("progressbar_file_%s" % device)
        prog.set_show_text(True)
        prog.set_pulse_step(0.01)
        prog.show()
        vbox.pack_start(prog)

        vbox.show()
        main_vbox.pack_start(vbox)

        self._file_prog[ device ] = prog
        self._file_lbl[ device ] = lbl

        return


    def _get_file_progress(self, device):
        if not self._file_prog.has_key(device):
            self._add_file_progress_ui(device)
        return self._file_prog[ device ]


    def _get_file_label(self, device):
        if not self._file_lbl.has_key(device):
            self._add_file_progress_ui(device)
        return self._file_lbl[ device ]


    def update_ui( self ):
        
        #print("update_ui: BEGIN" )
        
        if self._started and self.get_stats().is_updated():

            stats = self.update_ui_stats()

            stop = False

            if stats["files_count"] > 0 and stats["directories_searched"] != stats["directories_count"]:
                stop = True

            if stats["files_filtered"] > 0 and stats["files_processed"] != stats["files_filtered"]:
                stop = False

            if stats["archives_count"] > 0 and stats["archives_processed"] != stats["archives_count"]:
                stop = False

            if stop:
                self.get_ui().get_object("window1").set_title(
                    "%s" % self._ui_title )

                self.update_ui_stats()
                self.get_ui().get_object("button_start").emit("toggled")

        #print("update_ui: END" )
        return self._thread_ui_run
    
    def get_project( self ):
        if not self._project:
            self._project = Project()
            opts = self.get_option("project")
            self._project.connect(opts["adapter"], opts["host"], opts["user"], opts["passwd"], opts["db"], opts["options"])
        return self._project
    
    def do_start( self ):
        
        #print("do_start: BEGIN")
        
        self.get_stats().reset()
        self._last_stats = {}

        p = self.get_project()
        options = p.get_options( )
        
        if not options.has_key("directories"):
            options["directories"] = self._options["directories"]
        else:
            options["directories"] = json.loads(options["directories"])

        
        print("do_start: options=%r" % options)
        

        p.clean()

        for path in options["directories"]:
            st = os.stat(path)
            self.get_dirlist_queue("directories", st.st_dev).put_nowait( ( None, "", path ) )
        
        self._started = True
        
        #print("do_start: END")
        
        return True
    
    def do_stop( self ):

        from time import sleep

        if self._started:
            if self._threads:
                print("Stop threads...")
                for th in self._threads.values():
                    th.stop()

                print("Wait threads...")
                dead = False
                while not dead:
                    dead = True
                    for th in self._threads.values():
                        if th.is_alive():
                            dead = False
                    sleep(0.01)

            self._threads = {}

            self._clean_progress_ui()

            self.get_project().close_sessions()
            self._project = None
            self._queues = {}

            print("Stats:")
            pprint(self.get_stats().get_update())
            self._started = False
        return True
    
    def do_stop_ui( self ):
        if self._thread_ui:
            print("Stop UI thread...")
            self._thread_ui_run = False
            self._thread_ui = None
    
    def do_quit( self ):
        self.do_stop()
        self.do_stop_ui()
        
        p = self.get_project()
        p.close_sessions()
        
        print("Hide window...")
        self.get_ui().get_object("window1").hide()
        print("GTK main quit...")
        gtk.main_quit()
        print("GTK main quited...")
    
    def run( self ):
        gtk.gdk.threads_init()
        
        gtk.gdk.threads_enter()
        
        self._thread_ui = gobject.timeout_add( 100, self.update_ui )
        
        self.get_ui().get_object("window1").show()
        
        gtk.main()
        gtk.gdk.threads_leave()


    def on_button_start_toggled( self, widget, data = None):
        widget.set_sensitive(False)
        if self._started:
            self.do_stop()
            widget.set_label( gtk.STOCK_GO_FORWARD )
        else:
            self.do_start()
            widget.set_label( gtk.STOCK_STOP )
        widget.set_sensitive(True)


    def add_directory( self, dir ):
        store = self.get_ui().get_object("liststore1")
        for row in store:
            if row[0] == dir:
                return False
        store.append( [ dir ] )
        self.save_directories()
        return

    def save_directories( self ):
        store = self.get_ui().get_object("liststore1")
        dirs = []
        for row in store:
            dirs.append("%s" % row[0])
            
        p = self.get_project()
        p.save_option( "directories", json.dumps(dirs) )
        return


    def unload_project( self ):
        
        self.save_project()
        
        store = self.get_ui().get_object("liststore1")
        store.clear()
        
        treeview3 = self._ui.get_object("treeview3")
        store = treeview3.get_model()
        store.clear()
        
        self.get_ui().get_object("progressbar_all").set_fraction( 0.0 )

        filter = ",".join(self._options["filter_in"])
        self.get_ui().get_object("entry_filter_in").set_text(filter)
        
        filter = ",".join(self._options["filter_out"])
        self.get_ui().get_object("entry_filter_out").set_text(filter)

        self.get_ui().get_object("checkbutton_delete_to_trash").set_active(self._options["delete_to_trash"])
        return
    
    def save_project( self ):
        
        self.save_directories()
        
        p = self.get_project()

        filter = self.get_ui().get_object("entry_filter_in").get_text()
        p.save_option( "filter_in", json.dumps(filter.split(",")) )
        
        filter = self.get_ui().get_object("entry_filter_out").get_text()
        p.save_option( "filter_out", json.dumps(filter.split(",")) )

        p.save_option( "delete_to_trash", self.get_ui().get_object("checkbutton_delete_to_trash").get_active() )

        widget = self.get_ui().get_object("combobox_hash")
        model = widget.get_model()
        active = widget.get_active()

        if active >= 0:
            p.save_option( "hash_function", model[active][0] )

        return True
    
    def load_project( self ):

        print("load_project: self._options:")
        pprint(self._options)

        p = self.get_project()
        options = p.get_options( )

        print("load_project: options:")
        pprint(options)

        if options.has_key("filter_in"):
            filter = json.loads(options["filter_in"])
            self._options["filter_in"] = filter
            self.get_ui().get_object("entry_filter_in").set_text(",".join(filter))
        
        if options.has_key("filter_out"):
            filter = json.loads(options["filter_out"])
            self._options["filter_out"] = filter
            self.get_ui().get_object("entry_filter_out").set_text(",".join(filter))

        if options.has_key("delete_to_trash"):
            self._options["delete_to_trash"] = int(options["delete_to_trash"])
            self.get_ui().get_object("checkbutton_delete_to_trash").set_active(self._options["delete_to_trash"])

        if options.has_key("hash_function"):
            self._options["hash_function"] = options["hash_function"]

            widget = self.get_ui().get_object("combobox_hash")
            model = widget.get_model()

            count = 0
            iter = model.iter_children(None)
            while iter:
                item = model.get_value(iter, 0)
                if item == options["hash_function"]:
                    break

                count += 1
                iter = model.iter_next(iter)

            if iter:
                widget.set_active(count)
            else:
                widget.set_active(0)

        store = self.get_ui().get_object("liststore1")
        store.clear()
        
        treeview3 = self._ui.get_object("treeview3")
        store = treeview3.get_model()
        store.clear()
        
        # self.test_store()
        
        if not options.has_key("directories"):
            options["directories"] = self._options["directories"]
        else:
            options["directories"] = json.loads( options["directories"] )
        
        for dir in options["directories"]:
            self.add_directory( dir )

        #self._options.update(options)
        print("load_project: self._options:")
        pprint(self._options)

        return True
    
    
    def on_filechooserbutton_folder_file_set( self, widget, data = None):
        fn = widget.get_filename()
        self.add_directory( fn )
    
    def on_button_del_clicked( self, widget, data = None):
        tree = self.get_ui().get_object("treeview1")
        sel = tree.get_selection()
        store, rows = sel.get_selected_rows()
        for row in rows:
            store.remove( store.get_iter(row) )
        self.save_directories()
        return

    def on_button_clean_data_clicked( self, widget, data = None ):
        print("button_clean_data")
        p = self.get_project()
        p.clean()
        self.get_stats().reset()
        self._last_stats = {}
        self.update_ui_stats()
        self._clean_progress_ui()


    def idle_button_del_file_clicked( self, widget, data = None):
        print("idle_button_del_file")
        gobject.idle_add( self.button_del_file_clicked, widget, data )
    
    def button_del_file_clicked( self, widget, data = None):
        print("button_del_file")
        
        treeview = self._ui.get_object("treeview3")
        treeselection = treeview.get_selection()
        
        store, fiter = treeselection.get_selected()
        
        if fiter and store.iter_depth(fiter) > 0:
            fid = store.get_value(fiter, 1)
            path = store.get_value(fiter, 6)
            size = int(store.get_value(fiter, 9))
            
            p = self.get_project()
            #item = p.get_file( fid )
            hash_id = str( p.get_file_hash_id( fid ) )
            
            p.remove_file( fid )

            if self.get_option("delete_to_trash"):
                send2trash(path)
            else:
                os.unlink( path )
            
            store.remove(fiter)
            
            self.get_stats().dec("nonunique_count_now", 1)
            self.get_stats().dec("nonunique_size_now", size)
            self.update_ui_stats()
            
#            print("File #%s: '%s' removed" % (fid, path,))
#            print("Hash ID=%r" % hash_id)
            
            if hash_id:
                hiter = self.search( store, store.iter_children(None),
                    self.match_func, (0, hash_id,))
#                print("Root iter by hash: %r" % hiter)
                if hiter:
                    count = 0
                    fiter = store.iter_children(hiter)
                    while fiter:
                        count += 1
                        fiter = store.iter_next(fiter)
#                    print("Count children: %s" % count)
                    if count <= 1:
                        store.remove(hiter)
        
        return


    def _find_dupfiles(self):

        p = self.get_project()
        sess = p.get_session()

        treeview3 = self._ui.get_object("treeview3")
        store = treeview3.get_model()

        store.clear()

        files_and_hashes = p.get_nonuniq_files()

        #print("Duplicate files and hashes:")
        #pprint(files_and_hashes)

        for item in files_and_hashes:

            fitem = item["file"]
            hitem = item["hash"]

            hash_id_str = str(hitem.id)

            hiter = self.search( store, store.iter_children(None), self.match_func, (0, hash_id_str,) )

            if not hiter:
                hiter = store.append( None, [ hash_id_str, None, None, None, None, None, None, None, 0, 0, 0 ] )

            fiter = self.search( store, store.iter_children(None), self.match_func, (1, str( fitem.id ),) )

            if not fiter:
                md = datetime.fromtimestamp( fitem.mtime )
                f_s, f_u = self.format_file_size_si( fitem.size )
                store.append( hiter, [
                    None,
                    str(fitem.id),
                    self.idle_button_del_file_clicked,
                    md.strftime("%Y-%m-%d %H:%M:%S"),
                    "%0.3f %s" % (f_s, f_u,),
                    str(fitem.mime),
                    p.get_file_full_path( fitem ),
                    "Удалить", 2, 2, fitem.size ] )

            sess.expunge(fitem)


        stats = self.get_stats().get_update()
        ui = self.get_ui()

        nuq_sz, nuq_un = self.format_file_size_si( stats["nonunique_size"] )
        nuqn_sz, nuqn_un = self.format_file_size_si( stats["nonunique_size_now"] )

        gobject.idle_add( ui.get_object("progressbar_nonuniq").set_text,
            "Сейчас: %d (%0.3f%s); Найдено: %d (%0.3f%s)" % (
                stats["nonunique_count_now"], nuqn_sz, nuqn_un,
                stats["nonunique_count"], nuq_sz, nuq_un,
                )
        )

        return

    def on_button_find_dupfiles_clicked( self, widget, data = None):
        self._find_dupfiles()


    def on_button_find_dupimages_clicked( self, widget, data = None):
        pass


    def on_button_clean_clicked( self, widget, data = None):
        store = self.get_ui().get_object("liststore1")
        store.clear()
    
    def on_button_quit_clicked( self, widget, data = None):
        print("button_quit")
        self.save_project()
        self.do_quit()
    
    def on_window1_destroy_event( self, widget, data = None):
        print("window1_destroy")
        self.save_project()
        self.do_quit()
    
    def on_entry_filter_in_focus_out_event( self, widget, data = None):
        filter = widget.get_text().split(",")
        p = self.get_project()
        self._options["filter_in"] = filter
        p.save_option( "filter_in", json.dumps(filter) )
        return
    
    def on_entry_filter_out_focus_out_event( self, widget, data = None):
        filter = widget.get_text().split(",")
        p = self.get_project()
        self._options["filter_out"] = filter
        p.save_option( "filter_out", json.dumps(filter) )
        return

    def on_checkbutton_delete_to_trash_toggled(self, widget, data = None):
        p = self.get_project()
        self._options["delete_to_trash"] = widget.get_active()
        p.save_option( "delete_to_trash", widget.get_active() )
        return

    def on_combobox_hash_changed(self, widget, data = None):
        p = self.get_project()

        model = widget.get_model()
        active = widget.get_active()

        if active >= 0:
            self._options["hash_function"] = model[active][0]
            p.save_option( "hash_function", model[active][0] )
        return

    pass
